#include <fstream>
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <vector>
#include <sys/mman.h>
#include <fcntl.h>
#include "json.hpp"
#include "Spi.h"
#include "Struct.h"
#define CTPCON         "./"
pthread_cond_t doneCond; 
pthread_mutex_t doneMutex; 

extern pthread_cond_t doneCond; 
extern pthread_mutex_t doneMutex; 

using namespace std;
int main(int argc, char *argv[], char **env)
{
	string USER="user.json";
	if(argc>1)
		USER=argv[1];
	ifstream in;
	in.open(USER.c_str());
    string content((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
	in.close();
	nlohmann::json config = nlohmann::json::parse(content);
	char MD_FRONT[64],TD_FRONT[64],BROKER_ID[64],INVESTOR_ID[64],PASSWORD[64],APPID[64],AUTHCODE[64];
    string MARKET_DATA = config["MARKET_DATA"].get<string>();
	strcpy(MD_FRONT, config["MD_FRONT"].get<string>().c_str());
	strcpy(TD_FRONT, config["TD_FRONT"].get<string>().c_str());
	strcpy(INVESTOR_ID, config["INVESTOR_ID"].get<string>().c_str());
	strcpy(BROKER_ID, config["BROKER_ID"].get<string>().c_str());
	strcpy(PASSWORD,  config["PASSWORD"].get<string>().c_str());
	strcpy(APPID,     config["APPID"].get<string>().c_str());
	strcpy(AUTHCODE,  config["AUTHCODE"].get<string>().c_str());
	TdSpi* tdSpi=new TdSpi();
	for(auto& it:config["UNIVERSE"])
		tdSpi->add_universe(it.get<string>());
	ofstream log("log_md.txt");
	log<<"connecting to td front "<<TD_FRONT<<endl;
	CThostFtdcTraderApi* tradeAPI = CThostFtdcTraderApi::CreateFtdcTraderApi(CTPCON);	
	tradeAPI->RegisterSpi(tdSpi);
	tradeAPI->RegisterFront(TD_FRONT);
	tradeAPI->SubscribePublicTopic(THOST_TERT_QUICK);
	tradeAPI->SubscribePrivateTopic(THOST_TERT_QUICK);
	pthread_mutex_lock(&doneMutex);	
	tradeAPI->Init();
	pthread_cond_wait (&doneCond, &doneMutex); 
	pthread_mutex_unlock (&doneMutex); 

	int iRequestID=0,result=0;
	// Authorize 
	CThostFtdcReqAuthenticateField authField;
	memset(&authField, 0, sizeof(authField));
	strcpy(authField.BrokerID, BROKER_ID);
	strcpy(authField.UserID,   INVESTOR_ID);
	strcpy(authField.AppID,    APPID);
	strcpy(authField.AuthCode, AUTHCODE);
	pthread_mutex_lock(&doneMutex);
	tradeAPI->ReqAuthenticate(&authField, ++iRequestID);
	pthread_cond_wait (&doneCond, &doneMutex); 
	pthread_mutex_unlock (&doneMutex); 

	// Login 
	log<<"td loggin in\n"; 
	CThostFtdcReqUserLoginField req;
	memset(&req, 0, sizeof(req));
	strcpy(req.BrokerID, BROKER_ID);
	strcpy(req.UserID,   INVESTOR_ID);
	strcpy(req.Password, PASSWORD);
	pthread_mutex_lock(&doneMutex);
	tradeAPI->ReqUserLogin(&req, ++iRequestID);	
	pthread_cond_wait (&doneCond, &doneMutex); 
	pthread_mutex_unlock (&doneMutex);
	// Confirm SettlementInfo 
    log<<"settlement info confirming\n";
	CThostFtdcSettlementInfoConfirmField req1;
	memset(&req1, 0, sizeof(req1));
	strcpy(req1.BrokerID, BROKER_ID);
	strcpy(req1.InvestorID, INVESTOR_ID);
	pthread_mutex_lock(&doneMutex);
	result=tradeAPI->ReqSettlementInfoConfirm(&req1, ++iRequestID);
	pthread_cond_wait (&doneCond, &doneMutex); 
	pthread_mutex_unlock (&doneMutex); 


	log<<"connecting to md front "<<MD_FRONT<<endl;
    MdSpi* mdSpi = new MdSpi();
    CThostFtdcMdApi* mdAPI = CThostFtdcMdApi::CreateFtdcMdApi(CTPCON,false,false);
    mdAPI->RegisterSpi(mdSpi);
    mdAPI->RegisterFront(MD_FRONT);
    mdAPI->Init();
	while(1)
	{
		if(mdSpi->get_status()==1)
			break;
		usleep(1e4);
	}

	log<<"md logging in\n";
	CThostFtdcReqUserLoginField req2;
    memset(&req2, 0, sizeof(req2));
    mdAPI->ReqUserLogin(&req2, 2);
	while(1)
	{
		if(mdSpi->get_status()==2)
			break;
		usleep(1e4);
	}

	log<<"query instruments\n";
	CThostFtdcQryInstrumentField req4;
	memset(&req4, 0, sizeof(req4));
	pthread_mutex_lock(&doneMutex);
	tradeAPI->ReqQryInstrument(&req4, ++iRequestID);
	pthread_cond_wait (&doneCond, &doneMutex); 
	pthread_mutex_unlock (&doneMutex);
	vector<string> ids = tdSpi->get_list();
	int nid = 0;
	char** instruments=new char*[ids.size()];
	for(const auto& id:ids)
	{
		instruments[nid]=new char[16];
		strcpy(instruments[nid], id.c_str());
		nid+=1;

		string fn = MARKET_DATA + id + ".bin";
		FILE* fp = fopen(fn.c_str(),"ab+");
		Snapshot data;
		fseek(fp,0,SEEK_END);
		long len = ftell(fp) / sizeof(Snapshot);
		long step = len;
		long ptr = len;
		if(ptr>0)
		{
			while(ptr >= step)
			{
				ptr -= step;
				fseek(fp, ptr*sizeof(Snapshot),SEEK_SET);
				fread(&data,  sizeof(Snapshot),1, fp);
				if(data.nano > 0)
				{
					ptr += step;
					if(step == 1)
						break;
				}
				if( step >= 2 )
					step /= 2;
			}
		}
		long strech = ptr - ptr % BLOCK + 3 * BLOCK;
		cout<<id<<" "<<ptr<<" "<<len<<" "<<strech;
	
		if(len < strech)
		{
			memset(&data, 0, sizeof(Snapshot));
			for(long i = len; i < strech; i++)
				fwrite(&data, sizeof(Snapshot), 1,fp);
		}
		fclose(fp);
		int fh  =open(fn.c_str(), O_RDWR);
		Snapshot* snapshot = (Snapshot*)mmap(NULL, sizeof(Snapshot)*BLOCK*2, PROT_READ|PROT_WRITE,MAP_SHARED,fh, sizeof(Snapshot)*(ptr - ptr % BLOCK));
		close(fh);
		mdSpi->insert(id, snapshot + ptr % BLOCK);
		cout<<" mmmap\n";
		log<<id<<" "<<ptr<<" "<<len<<" "<<strech<<" "<<snapshot<<"\n";
	}
	log<<"Subscribe "<<nid<<" symbols\n";
	log.close();
	mdAPI->SubscribeMarketData(instruments, nid);
	while(1)
	{
	    if(tdSpi->is_closed())
			break;
		usleep(1e6);
	}
    return 0;
}
